{
 "cells": [
  {
   "cell_type": "code",
   "id": "initial_id",
   "metadata": {
    "collapsed": true,
    "ExecuteTime": {
     "end_time": "2025-02-08T02:37:23.947478Z",
     "start_time": "2025-02-08T02:37:22.962853Z"
    }
   },
   "source": "import torch",
   "outputs": [],
   "execution_count": 1
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# torch求导",
   "id": "804f590cf8486e46"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "### 近似求导",
   "id": "81d0aef17d20dd13"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-08T02:40:24.391666Z",
     "start_time": "2025-02-08T02:40:24.388253Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 定义函数\n",
    "def f(x):\n",
    "    return 3. * x ** 2 + 2. * x - 1\n",
    "\n",
    "\n",
    "# 近似求导：用定义,x移动eps单位，也就是离自己很近的一个点的切线\n",
    "def approximate_derivative(f, x, eps=1e-6):\n",
    "    return (f(x + eps) - f(x - eps)) / (2. * eps)"
   ],
   "id": "c0f09f500c56fd86",
   "outputs": [],
   "execution_count": 3
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-08T02:40:51.502446Z",
     "start_time": "2025-02-08T02:40:51.498981Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 求偏导数,其中一个数不动，对另外一个变量求导\n",
    "\n",
    "# 定义偏导函数\n",
    "def g(x1, x2):\n",
    "    return (x1 + 5) * (x2 ** 2)\n",
    "\n",
    "\n",
    "def approximate_gradient(g, x1, x2, eps=1e-6):\n",
    "    # 计算x1的导数\n",
    "    # lambda x: g(x, x2)表示g函数对x求导，即g(x1, x2)\n",
    "    dg_x1 = approximate_derivative(lambda x: g(x, x2), x1, eps)\n",
    "    # 计算x2的导数\n",
    "    dg_x2 = approximate_derivative(lambda x: g(x1, x), x2, eps)\n",
    "    return dg_x1, dg_x2"
   ],
   "id": "193808c5aacfc132",
   "outputs": [],
   "execution_count": 4
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "### torch近似求导",
   "id": "2ccb920fb5b5dd59"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-08T02:42:46.365871Z",
     "start_time": "2025-02-08T02:42:46.361557Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 声明两个tensor x1 和 x2，允许梯度计算，使用torch的自动求导上下文计算两个tensor的梯度\n",
    "# 使用 torch.autograd.grad 计算 y = g(x1, x2) 的偏导数\n",
    "\n",
    "# requires_grad=True 表示x1和x2需要求导\n",
    "x1 = torch.tensor([2.], requires_grad=True)\n",
    "x2 = torch.tensor([3.], requires_grad=True)\n",
    "y = g(x1, x2)\n",
    "\n",
    "# 计算y关于x1的导数\n",
    "(dy_dx1,) = torch.autograd.grad(y, x1, retain_graph=True)\n",
    "print(dy_dx1)\n"
   ],
   "id": "b7d709de54da25f2",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([9.])\n"
     ]
    }
   ],
   "execution_count": 16
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-08T02:42:46.657492Z",
     "start_time": "2025-02-08T02:42:46.654360Z"
    }
   },
   "cell_type": "code",
   "source": [
    "#不加retain_graph=True第二次执行会报错，原因是因为计算图已经被释放了\n",
    "try:\n",
    "    (dy_dx2,) = torch.autograd.grad(y, x2, retain_graph=True)\n",
    "    print(dy_dx2)\n",
    "except Exception as e:\n",
    "    print(e)"
   ],
   "id": "c9bb538a0a51bd74",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([42.])\n"
     ]
    }
   ],
   "execution_count": 17
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-08T02:43:03.084052Z",
     "start_time": "2025-02-08T02:43:03.079051Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 同时求导\n",
    "\n",
    "x1 = torch.tensor([2.], requires_grad=True)\n",
    "x2 = torch.tensor([3.], requires_grad=True)\n",
    "y = g(x1, x2)\n",
    "\n",
    "# 求偏导数\n",
    "dy_dx1, dy_dx2 = torch.autograd.grad(y, [x1, x2])\n",
    "\n",
    "print(dy_dx1, dy_dx2)"
   ],
   "id": "940192b963a5000c",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([9.]) tensor([42.])\n"
     ]
    }
   ],
   "execution_count": 18
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-08T02:43:25.595579Z",
     "start_time": "2025-02-08T02:43:25.590579Z"
    }
   },
   "cell_type": "code",
   "source": [
    "# 一般直接用 backward\n",
    "\n",
    "x1 = torch.tensor([2.], requires_grad=True)\n",
    "x2 = torch.tensor([3.], requires_grad=True)\n",
    "y = g(x1, x2)\n",
    "\n",
    "# 求偏导数,求梯度\n",
    "y.backward()\n",
    "print(x1.grad, x2.grad)"
   ],
   "id": "5ad8b91f789a9424",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor([9.]) tensor([42.])\n"
     ]
    }
   ],
   "execution_count": 19
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "### 二阶导",
   "id": "cce0096ca3c6a149"
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-08T02:44:17.774480Z",
     "start_time": "2025-02-08T02:44:17.769481Z"
    }
   },
   "cell_type": "code",
   "source": [
    "x1 = torch.tensor([2.], requires_grad=True)\n",
    "x2 = torch.tensor([3.], requires_grad=True)\n",
    "y = g(x1, x2)\n",
    "\n",
    "# 求y对x1和x2的二阶偏导数\n",
    "#，allow_unused 参数的作用是控制当 inputs 中的某些张量不需要梯度时，函数的行为方式。\n",
    "\n",
    "# 计算一阶导数\n",
    "dy_dx1, dy_dx2 = torch.autograd.grad(y, [x1, x2], create_graph=True)\n",
    "\n",
    "# 计算二阶导数\n",
    "# allow_unused=True 表示当 x1 和 x2 不是求导变量时，返回 None\n",
    "dy_dx1_dx1, dy_dx1_dx2 = torch.autograd.grad(dy_dx1, [x1, x2], allow_unused=True)\n",
    "dy_dx2_dx1, dy_dx2_dx2 = torch.autograd.grad(dy_dx2, [x1, x2], allow_unused=True)\n",
    "print(dy_dx1_dx1, dy_dx2_dx1, dy_dx2_dx2)"
   ],
   "id": "54e5bb8fd4929c31",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "None tensor([6.]) tensor([14.])\n"
     ]
    }
   ],
   "execution_count": 20
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-08T02:44:31.304606Z",
     "start_time": "2025-02-08T02:44:31.292729Z"
    }
   },
   "cell_type": "code",
   "source": [
    "#模拟梯度下降算法 SGD\n",
    "import torch\n",
    "\n",
    "learning_rate = 0.3\n",
    "x = torch.tensor(2.0, requires_grad=True)\n",
    "for _ in range(100):\n",
    "    z = f(x)\n",
    "    z.backward()\n",
    "    x.data.sub_(learning_rate * x.grad)  # x -= learning_rate * x.grad，这里就等价于optimizer.step()\n",
    "    x.grad.zero_()  # x.grad -= x.grad, x.grad = 0,梯度清零\n",
    "print(x)"
   ],
   "id": "ae6da58d92643e83",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(-0.3333, requires_grad=True)\n"
     ]
    }
   ],
   "execution_count": 21
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-08T02:44:40.565924Z",
     "start_time": "2025-02-08T02:44:39.905326Z"
    }
   },
   "cell_type": "code",
   "source": [
    "#GradientTape与optimizer（优化器）结合使用\n",
    "learning_rate = 0.01\n",
    "x = torch.tensor(2.0, requires_grad=True)\n",
    "optimizer = torch.optim.SGD([x], lr=learning_rate, momentum=0.9)\n",
    "for _ in range(500):\n",
    "    z = f(x)\n",
    "    optimizer.zero_grad()  # 梯度变为0\n",
    "    z.backward()  # dz/dx,求梯度\n",
    "    # print(x.grad)\n",
    "    optimizer.step()  # x -= learning_rate * x.grad\n",
    "\n",
    "print(x)\n"
   ],
   "id": "a4f5cf692960103f",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(-0.3333, requires_grad=True)\n"
     ]
    }
   ],
   "execution_count": 22
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": "",
   "id": "5ce772907acada6f"
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
